package com.senyint.hip.fhir.provider;

 
import java.util.ArrayList;
import java.util.Date;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.r4.model.Appointment;
import org.hl7.fhir.r4.model.Appointment.AppointmentParticipantComponent;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.OperationOutcome;
import org.springframework.stereotype.Component;

import ca.uhn.fhir.model.primitive.IdDt;
import ca.uhn.fhir.model.primitive.InstantDt;
import ca.uhn.fhir.parser.DataFormatException;
import ca.uhn.fhir.rest.annotation.Create;
import ca.uhn.fhir.rest.annotation.Delete;
import ca.uhn.fhir.rest.annotation.History;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Read;
import ca.uhn.fhir.rest.annotation.RequiredParam;
import ca.uhn.fhir.rest.annotation.ResourceParam;
import ca.uhn.fhir.rest.annotation.Search;
import ca.uhn.fhir.rest.annotation.Update;
import ca.uhn.fhir.rest.api.MethodOutcome;
import ca.uhn.fhir.rest.server.IResourceProvider;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;

/**
 * *******description*******
 * 预约
 * *************************
 *
 * @author lcg
 * @date 2019/07/01 16:46
 */
@Component
public class AppointmentProvider implements IResourceProvider {
	
	/**
	 * 临时存储数据
	 */
    private Map<Long, Deque<Appointment>> appointments = new HashMap<Long, Deque<Appointment>>();

    private long myNextId = 1;

	@Override
	public Class<Appointment> getResourceType() {
	 
		return Appointment.class;
	}
	
	 @Create()
	 public MethodOutcome createAppointment(@ResourceParam Appointment ap) {

	        long id = myNextId++;

	        String versionId = addNewVersionAp(ap, id);

	        MethodOutcome retVal = new MethodOutcome();
	        retVal.setId(new IdType("Appointment", String.valueOf(id), versionId));

	        // You can also add an OperationOutcome resource to return
	        // This part is optional though:
	        OperationOutcome outcome = new OperationOutcome();
	        outcome.setIdElement(new IdType("Appointment", String.valueOf(id), versionId));
	        outcome.addIssue().setDiagnostics("新增预约成功").setId(String.valueOf(id));
	        retVal.setOperationOutcome(outcome);

	        return retVal;
	    }
	
	
	 @Delete()
	 public MethodOutcome removeAppointment(@IdParam IdType theId) {
	        Long id;
	        try {
	            id = theId.getIdPartAsLong();
	        } catch (DataFormatException e) {
	            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
	        }

	        if (!appointments.containsKey(id)) {
	            throw new ResourceNotFoundException(theId);
	        }

	        appointments.remove(id);

	        MethodOutcome retVal = new MethodOutcome();
	        OperationOutcome outcome = new OperationOutcome();
	        outcome.addIssue().setDiagnostics("删除成功").setId(String.valueOf(id));
	        retVal.setOperationOutcome(outcome);

	        return retVal;
	    }
	 	
	    @Update()
	    public MethodOutcome updateAppointment(@IdParam IdType theId, @ResourceParam Appointment ap) {

	        Long id;
	        try {
	            id = theId.getIdPartAsLong();
	        } catch (DataFormatException e) {
	            throw new InvalidRequestException("Invalid ID " + theId.getValue() + " - Must be numeric");
	        }

	        /*
	         * Throw an exception (HTTP 404) if the ID is not known
	         */
	        if (!appointments.containsKey(id)) {
	            throw new ResourceNotFoundException(theId);
	        }

	        String versionId = addNewVersionAp(ap, id);

	        MethodOutcome retVal = new MethodOutcome();
	        retVal.setId(new IdType("Appointment", String.valueOf(id), versionId));

	        // You can also add an OperationOutcome resource to return
	        // This part is optional though:
	        OperationOutcome outcome = new OperationOutcome();
	        outcome.setIdElement(new IdType("Appointment", String.valueOf(id), versionId));
	        outcome.addIssue().setDiagnostics("更新预约成功").setId(String.valueOf(id));
	        retVal.setOperationOutcome(outcome);

	        return retVal;
	    }
	    
	    
	    
	    
	    @History()
	    public List<Appointment> readEncounterBundle(@IdParam IdType theId) {
	        Deque<Appointment> retVal;
	        try {
	            retVal = appointments.get(theId.getIdPartAsLong());
	        } catch (NumberFormatException e) {
	            /*
	             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
	             */
	            throw new ResourceNotFoundException(theId);
	        }

	        List<Appointment> apList = new ArrayList<>();
	        for (Appointment ap : retVal) {
	        	apList.add(ap);
	        }

	        return apList;
	    }
	
	    @Search
	    public List<Appointment> findDrUsingArbitraryCtriteria() {
	        LinkedList<Appointment> retVal = new LinkedList<Appointment>();

	        for (Deque<Appointment> nextApList : appointments.values()) {
	        	Appointment nextAp = nextApList.getLast();
	            retVal.add(nextAp);
	        }

	        return retVal;
	    }
	    
	    
	    
	    @Read(version = true)
	    public Appointment readAppointment(@IdParam IdType theId) {
	        Deque<Appointment> retVal;
	        try {
	            retVal = appointments.get(theId.getIdPartAsLong());
	        } catch (NumberFormatException e) {
	            /*
	             * If we can't parse the ID as a long, it's not valid so this is an unknown resource
	             */
	            throw new ResourceNotFoundException(theId);
	        }

	        if (theId.hasVersionIdPart() == false) {
	            return retVal.getLast();
	        } else {
	            for (Appointment nextVersion : retVal) {
	                String nextVersionId = nextVersion.getIdElement().getVersionIdPart();
	                if (theId.getVersionIdPart().equals(nextVersionId)) {
	                    return nextVersion;
	                }
	            }
	            // No matching version
	            throw new ResourceNotFoundException("Unknown version: " + theId.getValue());
	        }

	    }
	    
	    
	    @Search()
	    public List<Appointment> findByPatientName(@RequiredParam(name = "patient") String patientName) {

	        LinkedList<Appointment> retVal = new LinkedList<Appointment>();

	        NAMELOOP:
	        for (Deque<Appointment> nextAplist : appointments.values()) {
	        	Appointment nextAp = nextAplist.getLast();
	          
	             String patienN = nextAp.getParticipantFirstRep().getActor().getDisplay();
	         
	            if (StringUtils.isNotEmpty(patientName)) {
	            	 
	            	 if (patientName.equals(patienN)) {
	                     retVal.add(nextAp);
	                     break NAMELOOP;
	                 }
	            	 
	             } 
	           

	        }

	        return retVal;
	    }
	    
	    
	    
	    @Search()
	    public List<Appointment> findByPatientNameAndId(@RequiredParam(name = "patient") String patientName,@RequiredParam(name ="id") String id) {

	        LinkedList<Appointment> retVal = new LinkedList<Appointment>();

	        NAMELOOP:
	        for (Deque<Appointment> nextAplist : appointments.values()) {
	        	Appointment nextAp = nextAplist.getLast();
	             Long idPartAsLong = nextAp.getIdElement().getIdPartAsLong();
	             String patienN = nextAp.getParticipantFirstRep().getActor().getDisplay();
	         /*    for(AppointmentParticipantComponent app : nextAp.getParticipant()) {
            		 
            		 if( StringUtils.isNotEmpty(app.getActor().getReference()) &&  app.getActor().getReference().contains("Patient/")) {
            			 patienN=app.getActor().getDisplay();
            		 }
            	 }*/
	             
	             if(StringUtils.isNotEmpty(patientName)&&StringUtils.isNotEmpty(id)) {
	            	  
	            	 
	             
	            	 if (patientName.equals(patienN)&& id.equals(idPartAsLong.toString())) {
	                     retVal.add(nextAp);
	                     break NAMELOOP;
	                 }
	             }else if (StringUtils.isNotEmpty(patientName)&&StringUtils.isEmpty(id)) {
	            	 
	            	 if (patientName.equals(patienN)) {
	                     retVal.add(nextAp);
	                     break NAMELOOP;
	                 }
	            	 
	             }else if (StringUtils.isEmpty(patientName)&&StringUtils.isNotEmpty(id)) {
	            	 if (id.equals(idPartAsLong.toString())) {
	                     retVal.add(nextAp);
	                     break NAMELOOP;
	                 }
	             }
	           

	        }

	        return retVal;
	    }
	    
	    
	
	 private String addNewVersionAp(Appointment ap, long id) {

	        Date publishedDate;

	        if (!appointments.containsKey(id)) {
	        	appointments.put(id, new LinkedList<Appointment>());
	            publishedDate = InstantDt.withCurrentTime().getValue();
	        } else {
	        	Appointment oldAp = appointments.get(id).getLast();
	            publishedDate = oldAp.getMeta().getLastUpdated();

	        }


	        ap.getMeta().setLastUpdated(InstantDt.withCurrentTime().getValue());
	        Deque<Appointment> existingVersions = appointments.get(id);

	        String newVersion = Integer.toString(existingVersions.size());

	        //版本号
	        ap.getMeta().setVersionId(newVersion);

	        // Create an ID with the new version and assign it back to the resource
	        IdDt newId = new IdDt("Appointment", Long.toString(id), newVersion);
	        ap.setId(newId);

	        existingVersions.add(ap);
	        return newVersion;
	    }

}
